#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>

template<class T>
class BlockQueue
{
public:
    BlockQueue(int maxcap = defaultnum)
    :_maxcap(maxcap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
        pthread_cond_init(&_p_cond, nullptr);

        _low_water = _maxcap / 3;  // 6
        _high_water = _maxcap * 2 / 3;  // 13
    }

    void push(const T& data)
    {
        pthread_mutex_lock(&_mutex);

        // 判断的时候要防止线程被伪唤醒的情况,将if改为while
        // if(_q.size() == _maxcap)  // 不能你想生产就生产呀！你得先确保生产条件满足
        while(_q.size() == _maxcap)
        {
            // 让当前执行push的线程去条件变量下等待。

            // 资源不就绪，就等待。是持有锁等待的！持有锁等待会导致其他人也申请不到锁。因此要自动释放
            // 因为唤醒而返回的时候，重新持有锁！
            pthread_cond_wait(&_p_cond,&_mutex); // 1、调用的时候自动释放锁  2、处于阻塞状态

            // 如果线程wait时，被误唤醒了呢？多个线程会存在这种可能（伪唤醒），此时队列已经满了，在继续push的时候就有问题了。

        }
        // 1、队列没满
        // 2、被唤醒
        _q.push(data);    // 生产者生产完数据就能确保有数据，此时就可以唤醒消费者
        pthread_cond_signal(&_c_cond);       

        // if(_q.size() > _high_water)
        // {
        //     pthread_cond_signal(&_c_cond);       
        // }
        pthread_mutex_unlock(&_mutex);
    }

    T pop()
    {
        // 这里为什么要先加锁？再判断？
        // 因为判断临界资源条件是否满足，也是在访问临界资源！
        pthread_mutex_lock(&_mutex);
        while(_q.size() == 0)
        {
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        T data = _q.front();
        _q.pop();   // 消费者消费一个一定能保证当前一定有空间让生产者进行生产。
        // pthread_cond_signal(&_p_cond);

        if(_q.size() < _low_water)
        {
            pthread_cond_signal(&_p_cond);
        }
        pthread_mutex_unlock(&_mutex);
        return data;
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);
    }
private:
    std::queue<T> _q; // 共享资源
    int _maxcap;   // 极值
    pthread_mutex_t _mutex;
    pthread_cond_t _c_cond;
    pthread_cond_t _p_cond;
    // int _mincap;   // 极值
    static const int defaultnum = 20;

    int _low_water;
    int _high_water;
};